
using System;
using System.Diagnostics;
using System.IO;
using System.Windows.Forms;

namespace OpenComposite.Base
{

	public class TextBoxTraceListener : TraceListener
	{

		protected const string NO_CATEGORY = "";
		protected const string FAIL_CAT = "Fatal Error";
		protected const string SEPARATOR = "=========================";

		// A constructor supplies values to these fields.
		protected TextBox _traceTextBox;
		protected string _appTitle;
		protected string _logFileSpec;

		protected string _indentBuffer;
		protected string _lastLineAdded;

		// These are buffers for data from the Write() overloads.
		protected string _partialMessage;
		protected string _initCategory;

		protected bool _showTimeStamp = true;

		// This constructor also supplies a listener name and writes it to the trace.
		public TextBoxTraceListener(string name, TextBox traceBox, string title) : base(name)
		{
			InitializeListener(traceBox, title);
			this.WriteLine(string.Format("Listener \"{0}\" initialized.", this.Name));
		}

		// This is the basic constructor.
		public TextBoxTraceListener(TextBox traceBox, string title) : base()
		{
			InitializeListener(traceBox, title);
		}

		// This constructor overload also supplies a log file name.
		public TextBoxTraceListener(string name, TextBox traceBox, string title, string logFile) :
			this(name, traceBox, title)
		{
			_logFileSpec = logFile;
		}

		// This constructor overload also supplies listener and log file names.
		public TextBoxTraceListener(TextBox traceBox, string title, string logFile) :
			this(traceBox, title)
		{
			_logFileSpec = logFile;
		}

		protected void InitializeListener(TextBox traceBox, string title)
		{

			// Save a reference to the ListView and the application title.
			_traceTextBox = traceBox;
			_appTitle = title;

			ResetPartialMessage();
		}

		public bool ShowTimeStamp
		{
			get { return _showTimeStamp; }
			set { _showTimeStamp = value; }
		}

		public override void Write(object obj)
		{
			if ( obj == null )
				this.Write("", NO_CATEGORY);
			else
				this.Write(obj.ToString(), NO_CATEGORY);
		}

		public override void Write(string message)
		{
			this.Write(message, NO_CATEGORY);
		}

		public override void Write(object obj, string category)
		{
			if ( obj == null )
				this.Write("", category);
			else
				this.Write(obj.ToString(), category);
		}

		public override void Write(string message, string category)
		{
			// Save the partial trace message and initial category for later use.
			_partialMessage += message;
			if ( category != NO_CATEGORY )
				_initCategory = category;
		}

		public override void WriteLine(object obj)
		{
			if ( obj == null )
				this.WriteLine((string)null, NO_CATEGORY);
			else
				this.WriteLine(obj.ToString(), NO_CATEGORY);
		}

		public override void WriteLine(string message)
		{
			// If the message is null, write a separator to the trace.
			if ( message == null ) {
				// Flush a partial trace message if it is present, 
				// then write the separator.
				this.Flush();
				setText(SEPARATOR + Environment.NewLine);
				this.NeedIndent = true;
			} else {
				// Otherwise, write the message with no category.
				this.WriteLine(message, NO_CATEGORY);
			}
		}

		public override void WriteLine(object obj, string category)
		{
			if ( obj == null )
				this.WriteLine("", category);
			else
				this.WriteLine(obj.ToString(), category);
		}

		public override void WriteLine(string message, string category)
		{
			// Form the indent string.
			this.WriteIndent();

			// If there is no partial trace message, just write the current message.
			if ( _partialMessage == "" ) {
				_lastLineAdded = (_showTimeStamp ? this.TimeStamp() + ": " : "") +
					(category != "" ? category + ": " : "") +
					_indentBuffer + message +
					Environment.NewLine;
			} else {
				// Include the partial trace message in the written line.
				_lastLineAdded = (_showTimeStamp ? this.TimeStamp() + ": " : "") +
				(category != NO_CATEGORY ? category : _initCategory) + ": " +
                _indentBuffer + _partialMessage + message +
				Environment.NewLine;
				ResetPartialMessage();
			}

			setText(_lastLineAdded);
			this.NeedIndent = true;
		}

		protected void ResetPartialMessage()
		{
			// Reset the partial trace message after calls to 
			// WriteLine() and Flush().
			_partialMessage = "";
			_initCategory = NO_CATEGORY;
		}

		public override void Flush()
		{
			// If there is a partial trace message waiting, write it.
			if ( _partialMessage != "" ) {
				// Form the indent string, then make the 
				// partial trace message into a written line.
				this.WriteIndent();
				_lastLineAdded = (_showTimeStamp ? this.TimeStamp() + ": " : "") +
								_initCategory + ": " + _indentBuffer + _partialMessage;
				setText(_lastLineAdded);

				ResetPartialMessage();
				this.NeedIndent = true;
			}
		}

		public override void Fail(string message)
		{
			// Flush the partial trace message and 
			// inhibit indenting of the Fail message.
			this.Flush();
			this.NeedIndent = false;

			this.WriteLine(message, FAIL_CAT);
			MessageBox.Show(message, _appTitle, MessageBoxButtons.OK, MessageBoxIcon.Stop);

			this.NeedIndent = true;
		}

		public override void Fail(string message, string detailMessage)
		{
			// Flush the partial trace message and 
			// inhibit indenting of the Fail message.
			this.Flush();
			this.NeedIndent = false;

			this.WriteLine(message, FAIL_CAT);

			// Handle the Trace.Fail( string ) overload, if necessary.
			if ( detailMessage == null ) {
				MessageBox.Show(message, _appTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			} else {
				// Write the detailMessage to the ListView.
				setText(detailMessage);
				MessageBox.Show(message + "\r\n\r\n" + detailMessage,
					_appTitle, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
			}

			this.NeedIndent = true;
		}

		public override void Close()
		{
			// If a log file is specified, copy the ListView data to the file.
			if ( _logFileSpec != "" ) {
				StreamWriter writer = File.AppendText(_logFileSpec);

				writer.Write(_traceTextBox.Text);

				// Flush the log file buffer and close the file.
				writer.Flush();
				writer.Close();
			}

			base.Close();
		}

		protected override void WriteIndent()
		{
			// Generate the correct number of spaces for the indent string.
			if ( this.NeedIndent )
				_indentBuffer = new string(' ', this.IndentLevel * this.IndentSize);
			else
				_indentBuffer = "";
		}

		protected string TimeStamp()
		{
			return DateTime.Now.ToString("HH:mm:ss.fff");
		}

		private void setText(string txt)
		{
			_traceTextBox.Text += txt;
			_traceTextBox.Select(_traceTextBox.TextLength, 0);
			_traceTextBox.ScrollToCaret();
		}
	}
}